

function arpcAlertFatalError(sMessage)
{
  if(sMessage)
  {
    alert("ERROR: " + sMessage + "!");
  }
}

function arpcFault(iFaultCode, sFaultString)
{
  this.iFaultCode = iFaultCode;
  this.sFaultString = sFaultString;
}

function arpcCreateXmlHttpRequest()
{
  var oXmlHttpRequest = undefined;
  
  do
  {
    try
    {
      oXmlHttpRequest = new XMLHttpRequest();
      if(oXmlHttpRequest) break;
    }
    catch(e) {}
    
    try
    {
      oXmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
      if(oXmlHttpRequest) break;
    }
    catch(e) {}

    try
    {
      oXmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
      if(oXmlHttpRequest) break;
    }
    catch(e) {}
  }
  while(false);
  
  return oXmlHttpRequest;
}

function arpcExtractBasicType(oXml, sDesiredBasicType)
{
  var sValue = undefined;
  var oTemp = oXml;
  
  if( oTemp &&
      (sDesiredBasicType == oTemp.nodeName) &&
      (1 == oTemp.nodeType) &&
      (oTemp = oTemp.childNodes) &&
      (1 == oTemp.length) &&
      (oTemp = oTemp[0]) &&
      (3 == oTemp.nodeType)
    )
  {
    sValue = oTemp.nodeValue;
  }
  
  return sValue;
}

function arpcXmlToInt(oXml)
{
  var iInt = undefined;
  var sValue;
  
  if( (sValue = arpcExtractBasicType(oXml, "int")) || 
      (sValue = arpcExtractBasicType(oXml, "i4")) )
  {
    iInt = parseInt(sValue);
  }
  
  return iInt;
}

function arpcXmlToBoolean(oXml)
{
  var bBoolean = undefined;
  var sValue;
  
  if(sValue = arpcExtractBasicType(oXml, "boolean"))
  {
    if("0" == sValue)
    {
      bBoolean = false;
    }
    else("1" == sValue)
    {
      bBoolean = true;
    }
  }
  
  return bBoolean;
}

function arpcXmlToString(oXml)
{
  var sString = undefined;
  
  if(sString = arpcExtractBasicType(oXml, "string"))
  {
    sString.replace("&lt;", "<");
    sString.replace("&amp;", "&");
  } 
  
  return sString;
}

function arpcXmlToDouble(oXml)
{
  var dDouble = undefined;
  var sValue;
  
  if(sValue = arpcExtractBasicType(oXml, "double")) 
  {
    dDouble = parseFloat(sValue);
  }
  
  return dDouble;
}

function arpcXmlToDate(oXml)
{
  var oDate = undefined;
  var sValue;
  
  if(sValue = arpcExtractBasicType(oXml, "dateTime.iso8601")) 
  {
    oDate = new Date();
    oDate.setISO8601(sValue);
  }
  
  return oDate;
}

function arpcNameValue(sName, oXmlValue)
{
  this.sName = sName;
  this.oXmlValue = oXmlValue;
}

function arpcXmlToStruct(oXml)
{
  var aoNameValueArray = undefined;
  var oTemp = oXml;
  
  if( oTemp &&
      ("struct" == oTemp.nodeName) &&
      (1 == oTemp.nodeType) &&
      (oTemp = oTemp.childNodes)
    )
  {
    var iNameValueArraySize = oTemp.length;
    aoNameValueArray = new Array(iNameValueArraySize);
    for(var i = 0; i < iNameValueArraySize; i++)
    {
      var oTemp1 = oTemp[i];
      if( oTemp1 &&
          ("member" == oTemp1.nodeName) &&
          (1 == oTemp1.nodeType) &&
          (oTemp1 = oTemp1.childNodes) &&
          (2 == oTemp1.length)
        )
      {
        var sName = undefined;
        var oXmlValue = undefined;
        
        for(var j = 0; j < 2; j++)
        {
          var oTemp2 = oTemp1[j];
          if(oTemp2)
          {
            if( ("name" == oTemp2.nodeName) &&
                (1 == oTemp2.nodeType) &&
                (oTemp2 = oTemp2.childNodes) &&
                (1 == oTemp2.length) &&
                (oTemp2 = oTemp2[0]) &&
                (3 == oTemp2.nodeType)
              )
            {
              sName = oTemp2.nodeValue;
            }
            else if( ("value" == oTemp2.nodeName) &&
                      (1 == oTemp2.nodeType) &&
                      (oTemp2 = oTemp2.childNodes) &&
                      (1 == oTemp2.length) &&
                      (oTemp2 = oTemp2[0]) &&
                      (1 === oTemp2.nodeType)
                    )
            {
              oXmlValue = oTemp2;
            }
            else
            {
              return undefined;
            }
          }
          else
          {
            return undefined;
          }
        }
        
        if( (undefined != sName) && (undefined != oXmlValue) )
        {
          aoNameValueArray[i] = new arpcNameValue(sName, oXmlValue);
        }
        else
        {
          return undefined;
        }
      }
      else
      {
        return undefined;
      }
    }
  }
  
  return aoNameValueArray;
}

function arpcXmlToArray(oXml, funcXmlTo)
{
  var aArray = undefined;
  var oTemp = oXml;
  
  if( oTemp &&
      ("array" == oTemp.nodeName) &&
      (1 == oTemp.nodeType) &&
      (oTemp = oTemp.childNodes) &&
      (1 == oTemp.length) &&
      (oTemp = oTemp[0]) &&
      ("data" == oTemp.nodeName) &&
      (1 == oTemp.nodeType) &&
      (oTemp = oTemp.childNodes)
    )
  {
    var iArraySize = oTemp.length;
    aArray = new Array(iArraySize);
    for(var i = 0; i < iArraySize; i++)
    {
      var oTemp1 = oTemp[i];
      var temp;
      
      if(oTemp1 &&
          ("value" == oTemp1.nodeName) &&
          (1 == oTemp1.nodeType) &&
          (oTemp1 = oTemp1.childNodes) &&
          (1 == oTemp1.length) &&
          (oTemp1 = oTemp1[0]) &&
          (undefined != (temp = funcXmlTo(oTemp1))) 
        )
      {
        aArray[i] = temp;
      }
      else
      {
        return undefined;
      }
    }
  }  
  
  return aArray;
}

function arpcXmlToFault(oXml)
{
  var oArpcFault = undefined;
  var iFaultCode = undefined;
  var sFaultName = undefined;
  var aoNameValueArray = arpcXmlToStruct(oXml);
  
  if( (undefined != aoNameValueArray) &&
      (2 == aoNameValueArray.length)
    )
  {
    for(var i = 0; i < 2; i++)
    {
      switch(aoNameValueArray[i].sName)
      {
        case "faultCode":
        {
          iFaultCode = arpcXmlToInt(aoNameValueArray[i].oXmlValue);
          break;
        }
        
        case "faultString":
        {
          sFaultString = arpcXmlToString(aoNameValueArray[i].oXmlValue);
          break;
        }
        
        default:
        {
          return undefined;
          break;
        }
      }
    }
    
    if( (undefined != iFaultCode) && (undefined != sFaultString) )
    {
      oArpcFault = new arpcFault(iFaultCode, sFaultString);
    }
  }
  
  return oArpcFault;
}

function arpcIntToXml(iInteger)
{
  return ("<int>" + iInteger + "</int>");
}

function arpcBooleanToXml(bBoolean)
{
  return ("<boolean>" + (bBoolean ? "1" : "0") + "</boolean>");
}

function arpcStringToXml(sString)
{
  sString.replace("&", "&amp;");
  sString.replace("<", "&lt;");
  return ("<string>" + sString + "</string>");
}

function arpcDoubleToXml(dDouble)
{
  return ("<double>" + dDobule + "</double>");
}

function arpcDateToXml(oDate)
{
  return ("<dateTime.iso8601>" + oDate.toISO8601String() + "</dateTime.iso8601>");
}

function arpcArrayToXml(aArray, funcToXml)
{
  var sXml = "";
  
  sXml += "<array>";
  sXml += "<data>";
  
  for(var i = 0; i < aArray.length; i++)
  {
    sXml += "<value>" + funcToXml(aArray[i]) + "</value>";
  }
  
  sXml += "</data>";
  sXml += "</array>";
  
  return sXml;
}

function arpcCallMethod(sUrl, sMethodName, sParameterXml, funcCallBack)
{
  // validate parameters ...
  if(!sUrl)
  {
    arpcAlertFatalError("arpcCallMethod - invalid 'sUrl' parameter");
    return;  
  }
  
  if(!sParameterXml)
  {
    arpcAlertFatalError("arpcCallMethod - invalid 'sParameterXml' parameter");  
    return;  
  }
  
  if(!sMethodName)
  {
    arpcAlertFatalError("arpcCallMethod - invalid 'sMethodName' parameter");  
    return;  
  }
  
  if(!funcCallBack)
  {
    arpcAlertFatalError("arpcCallMethod - invalid 'funcCallBack' parameter");
    return;  
  }
  
  // create and setup xmlHttpRequest ...
  var oXmlHttpRequest = arpcCreateXmlHttpRequest();
  if(!oXmlHttpRequest)
  {
    funcCallback(null, new arpcFault(0, "Could not create xmlHttpRequest"));
    return;
  }
  
  oXmlHttpRequest.open("POST", sUrl + "?" + new Date().getTime(), true);
  oXmlHttpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  
  oXmlHttpRequest.onreadystatechange = function()
  {
    switch(oXmlHttpRequest.readyState)
    {
      case 4:
      {
        var iStatus = oXmlHttpRequest.status;
        switch(iStatus)
        {
          case 200:
          {
            // handle response ...
            var oTemp = oXmlHttpRequest.responseXML;
            //alert(oTemp.childNodes[0].nodeName);
            if( oTemp && 
                (9 == oTemp.nodeType) &&
                (oTemp = oTemp.childNodes) &&
                (1 == oTemp.length) &&
                (oTemp = oTemp[0]) &&
                (1 == oTemp.nodeType) &&
                ("methodResponse" == oTemp.nodeName) &&
                (oTemp = oTemp.childNodes) &&
                (1 == oTemp.length) &&
                (oTemp = oTemp[0]) &&
                (1 == oTemp.nodeType)
              )
            {
              if( ("params" == oTemp.nodeName) &&
                  (oTemp = oTemp.childNodes) &&
                  (1 == oTemp.length) &&
                  (oTemp = oTemp[0]) &&
                  (1 == oTemp.nodeType) &&
                  ("param" == oTemp.nodeName) &&
                  (oTemp = oTemp.childNodes) &&
                  (1 == oTemp.length) &&
                  (oTemp = oTemp[0]) &&
                  (1 == oTemp.nodeType) &&
                  ("value" == oTemp.nodeName) &&
                  (oTemp = oTemp.childNodes) &&
                  (1 == oTemp.length) &&
                  (oTemp = oTemp[0]) &&
                  (1 == oTemp.nodeType)
                )
              {
                funcCallBack(oTemp, null);
              }
              else if( ("fault" == oTemp.nodeName) &&
                        (oTemp = oTemp.childNodes) &&
                        (1 == oTemp.length) &&
                        (oTemp = oTemp[0]) &&
                        (1 == oTemp.nodeType) &&
                        ("value" == oTemp.nodeName) &&
                        (oTemp = oTemp.childNodes) &&
                        (1 == oTemp.length) &&
                        (oTemp = oTemp[0]) &&
                        (1 == oTemp.nodeType) &&
                        (undefined != (oTemp = arpcXmlToFault(oTemp)))
                      )
              {
                funcCallBack(null, oTemp);
              }
              else
              {
                funcCallBack(null, new arpcFault(0, "Invalid repsonse"));
              }
            }
            else
            {
              funcCallBack(null, new arpcFault(0, "Invalid response"));
            }

            break;
          }
          
          default:
          {
            // handle http error ...

            funcCallBack(null, new arpcFault(0, "HTTP error: " + iStatus + "(" + oXmlHttpRequest.statusText + ")"));
            break;
          }
        }
      }
      
      default:
        break;
    }
  }
  
  // create raw post xml ...
  var sSendXml = "";
  
  sSendXml += "<?xml version=\"1.0\"?>";
  sSendXml += "<methodCall>";
  sSendXml += "<methodName>" + sMethodName + "</methodName>";
  sSendXml += "<params>";
  sSendXml += "<param><value>" + sParameterXml + "</value></param>";
  sSendXml += "</params>";
  sSendXml += "</methodCall>";
  
  oXmlHttpRequest.send(sSendXml);
}